/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2002-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: SecuritySupport.java,v 1.1.2.1 2005/08/01 02:08:48 jeffsuttor Exp $
 */

package com.sun.org.apache.xalan.internal.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Properties;

/**
 * This class is duplicated for each subpackage so keep it in sync. It is
 * package private and therefore is not exposed as part of any API.
 *
 * @xerces.internal
 */
public final class SecuritySupport {

  private static final SecuritySupport securitySupport = new SecuritySupport();

  /**
   * Return an instance of this class.
   */
  public static SecuritySupport getInstance() {
    return securitySupport;
  }

  public static ClassLoader getContextClassLoader() {
    return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        ClassLoader cl = null;
        try {
          cl = Thread.currentThread().getContextClassLoader();
        } catch (SecurityException ex) {
        }
        return cl;
      }
    });
  }

  static ClassLoader getSystemClassLoader() {
    return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        ClassLoader cl = null;
        try {
          cl = ClassLoader.getSystemClassLoader();
        } catch (SecurityException ex) {
        }
        return cl;
      }
    });
  }

  static ClassLoader getParentClassLoader(final ClassLoader cl) {
    return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        ClassLoader parent = null;
        try {
          parent = cl.getParent();
        } catch (SecurityException ex) {
        }

        // eliminate loops in case of the boot
        // ClassLoader returning itself as a parent
        return (parent == cl) ? null : parent;
      }
    });
  }

  public static String getSystemProperty(final String propName) {
    return (String) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return System.getProperty(propName);
      }
    });
  }

  public static String getSystemProperty(final String propName, final String def) {
    return (String) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return System.getProperty(propName, def);
      }
    });
  }

  static FileInputStream getFileInputStream(final File file)
      throws FileNotFoundException {
    try {
      return (FileInputStream) AccessController.doPrivileged(new PrivilegedExceptionAction() {
        public Object run() throws FileNotFoundException {
          return new FileInputStream(file);
        }
      });
    } catch (PrivilegedActionException e) {
      throw (FileNotFoundException) e.getException();
    }
  }

  /**
   * Return resource using the same classloader for the ObjectFactory by
   * default or bootclassloader when Security Manager is in place
   */
  public static InputStream getResourceAsStream(final String name) {
    if (System.getSecurityManager() != null) {
      return getResourceAsStream(null, name);
    } else {
      return getResourceAsStream(ObjectFactory.findClassLoader(), name);
    }
  }

  public static InputStream getResourceAsStream(final ClassLoader cl,
      final String name) {
    return (InputStream) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        InputStream ris;
        if (cl == null) {
          ris = Object.class.getResourceAsStream("/" + name);
        } else {
          ris = cl.getResourceAsStream(name);
        }
        return ris;
      }
    });
  }

  /**
   * Gets a resource bundle using the specified base name, the default locale, and the caller's
   * class loader.
   *
   * @param bundle the base name of the resource bundle, a fully qualified class name
   * @return a resource bundle for the given base name and the default locale
   */
  public static ListResourceBundle getResourceBundle(String bundle) {
    return getResourceBundle(bundle, Locale.getDefault());
  }

  /**
   * Gets a resource bundle using the specified base name and locale, and the caller's class loader.
   *
   * @param bundle the base name of the resource bundle, a fully qualified class name
   * @param locale the locale for which a resource bundle is desired
   * @return a resource bundle for the given base name and locale
   */
  public static ListResourceBundle getResourceBundle(final String bundle, final Locale locale) {
    return AccessController.doPrivileged(new PrivilegedAction<ListResourceBundle>() {
      public ListResourceBundle run() {
        try {
          return (ListResourceBundle) ResourceBundle.getBundle(bundle, locale);
        } catch (MissingResourceException e) {
          try {
            return (ListResourceBundle) ResourceBundle.getBundle(bundle, new Locale("en", "US"));
          } catch (MissingResourceException e2) {
            throw new MissingResourceException(
                "Could not load any resource bundle by " + bundle, bundle, "");
          }
        }
      }
    });
  }

  public static boolean getFileExists(final File f) {
    return ((Boolean) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return f.exists() ? Boolean.TRUE : Boolean.FALSE;
      }
    })).booleanValue();
  }

  static long getLastModified(final File f) {
    return ((Long) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return new Long(f.lastModified());
      }
    })).longValue();
  }

  /**
   * Strip off path from an URI
   *
   * @param uri an URI with full path
   * @return the file name only
   */
  public static String sanitizePath(String uri) {
    if (uri == null) {
      return "";
    }
    int i = uri.lastIndexOf("/");
    if (i > 0) {
      return uri.substring(i + 1, uri.length());
    }
    return "";
  }

  /**
   * Check the protocol used in the systemId against allowed protocols
   *
   * @param systemId the Id of the URI
   * @param allowedProtocols a list of allowed protocols separated by comma
   * @param accessAny keyword to indicate allowing any protocol
   * @return the name of the protocol if rejected, null otherwise
   */
  public static String checkAccess(String systemId, String allowedProtocols, String accessAny)
      throws IOException {
    if (systemId == null || (allowedProtocols != null &&
        allowedProtocols.equalsIgnoreCase(accessAny))) {
      return null;
    }

    String protocol;
    if (systemId.indexOf(":") == -1) {
      protocol = "file";
    } else {
      URL url = new URL(systemId);
      protocol = url.getProtocol();
      if (protocol.equalsIgnoreCase("jar")) {
        String path = url.getPath();
        protocol = path.substring(0, path.indexOf(":"));
      }
    }

    if (isProtocolAllowed(protocol, allowedProtocols)) {
      //access allowed
      return null;
    } else {
      return protocol;
    }
  }

  /**
   * Check if the protocol is in the allowed list of protocols. The check
   * is case-insensitive while ignoring whitespaces.
   *
   * @param protocol a protocol
   * @param allowedProtocols a list of allowed protocols
   * @return true if the protocol is in the list
   */
  private static boolean isProtocolAllowed(String protocol, String allowedProtocols) {
    if (allowedProtocols == null) {
      return false;
    }
    String temp[] = allowedProtocols.split(",");
    for (String t : temp) {
      t = t.trim();
      if (t.equalsIgnoreCase(protocol)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Read JAXP system property in this order: system property,
   * $java.home/lib/jaxp.properties if the system property is not specified
   *
   * @param propertyId the Id of the property
   * @return the value of the property
   */
  public static String getJAXPSystemProperty(String sysPropertyId) {
    String accessExternal = getSystemProperty(sysPropertyId);
    if (accessExternal == null) {
      accessExternal = readJAXPProperty(sysPropertyId);
    }
    return accessExternal;
  }

  /**
   * Read from $java.home/lib/jaxp.properties for the specified property
   * The program
   *
   * @param propertyId the Id of the property
   * @return the value of the property
   */
  static String readJAXPProperty(String propertyId) {
    String value = null;
    InputStream is = null;
    try {
      if (firstTime) {
        synchronized (cacheProps) {
          if (firstTime) {
            String configFile = getSystemProperty("java.home") + File.separator +
                "lib" + File.separator + "jaxp.properties";
            File f = new File(configFile);
            if (getFileExists(f)) {
              is = getFileInputStream(f);
              cacheProps.load(is);
            }
            firstTime = false;
          }
        }
      }
      value = cacheProps.getProperty(propertyId);

    } catch (Exception ex) {
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (IOException ex) {
        }
      }
    }

    return value;
  }

  /**
   * Cache for properties in java.home/lib/jaxp.properties
   */
  static final Properties cacheProps = new Properties();

  /**
   * Flag indicating if the program has tried reading java.home/lib/jaxp.properties
   */
  static volatile boolean firstTime = true;

  private SecuritySupport() {
  }
}
